package com.api.rest;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

import com.api.rest.model.RestClass;
import com.api.rest.model.RestMethod;
import com.api.rest.model.RestParameter;

public class RestHandlerImpl implements RestHandler {

	@Override
	public RestClass getRestClass(Class<?> aClass) {

		String name = aClass.getName();
		String simpleName = aClass.getSimpleName();

		List<RestMethod> methods = new ArrayList<RestMethod>();
		for (Method method : aClass.getDeclaredMethods()) {
			RestMethod restMethod = getRestMethod(method);
			methods.add(restMethod);
		}

		return new RestClass(name, simpleName, methods);
	}

	@Override
	public RestMethod getRestMethod(Method method) {

		String name = method.getName();
		String returnType = method.getGenericReturnType().toString();

		List<String> throwTypes = new ArrayList<String>();
		for (Class<?> exception : method.getExceptionTypes()) {
			throwTypes.add(exception.getName());
		}

		List<RestParameter> parameters = new ArrayList<RestParameter>();
		Type[] genericParameterTypes = method.getGenericParameterTypes();
		Class<?>[] paramClasses = method.getParameterTypes();
		for (int i = 0; i < paramClasses.length; i++) {
			RestParameter parameter = getRestParameter(paramClasses[i], genericParameterTypes[i]);
			parameters.add(parameter);
		}

		boolean deprecated = (method.getAnnotation(Deprecated.class) != null);

		RestMethod restMethod = new RestMethod();
		restMethod.setName(name);
		restMethod.setReturnType(returnType);
		restMethod.setThrowTypes(throwTypes);

		restMethod.setParameters(parameters);
		restMethod.setDeprecated(deprecated);

		return restMethod;
	}

	private RestParameter getRestParameter(Class<?> typeClass, Type type) {

		String paramType = typeClass.getSimpleName();
		boolean isEnum = typeClass.isEnum();
		List<RestParameter> genericTypes = null;

		if (type instanceof ParameterizedType) {
			genericTypes = new ArrayList<RestParameter>();

			ParameterizedType aType = (ParameterizedType) type;
			Type[] parameterArgTypes = aType.getActualTypeArguments();
			for (Type parameterArgType : parameterArgTypes) {
				Class<?> parameterArgClass = (Class<?>) parameterArgType;
				genericTypes.add(getRestParameter(parameterArgClass, parameterArgType));
			}
		}
		return new RestParameter(paramType, typeClass.getName(), isEnum, genericTypes);
	}
}
